/*******************************************************************************
this code is protected by the GNU affero GPLv3
author:Sylvain BERTRAND <sylvain.bertrand AT gmail dot com>
*******************************************************************************/
#include <ulinux/compiler_misc.h>
#include <ulinux/compiler_types.h>
#include <ulinux/sysc.h>
#include <ulinux/types.h>
#include <ulinux/error.h>
#include <ulinux/signal/signal.h>
#include <ulinux/signal/siginfo.h>
#include <ulinux/wait.h>

#define INIT_C
#include "out.h"
#undef INIT_C
#include "ulinux_namespace.h"

static void sigs_setup(void)
{
  ul mask=(~0);
  l r;

  OUT(PRE "setting up signals...\n");
  r=rt_sigprocmask(SIG_BLOCK,&mask,sizeof(mask));
  if(ISERR(r)){
    OUT("ERROR:unable to block all signals (except KILL and STOP)\n");
    exit_group(-1);
  }
  OUT("done\n");
}

static i sysstart_clone(void)
{
  l r;
  ul mask;

  OUT(PRE "clone and execve /bin/sysstart...\n");
  r=clone(SIGCHLD);
  if(ISERR(r)){
    OUT("ERROR(%ld):unable to clone sysinit for /bin/sysstart\n",r);
    exit_group(-1);
  }

  if(r) return (i)r;

  mask=(~0);
  r=rt_sigprocmask(SIG_UNBLOCK,&mask,sizeof(mask));
  if(ISERR(r)){
    OUT("ERROR(%ld):unable to unblock all signals for /bin/sysstart\n",r);
    exit_group(-1);
  }

  r=execve("/bin/sysstart",0);
  if(ISERR(r)) {OUT("ERROR(%ld):unable to execve /bin/sysstart\n",r);}
  exit_group(-1);
#ifdef __GNUC__
  return 0;/*unreachable not detected at the end of function*/
#endif
}

#ifndef NO_TTY
static i getty_spawn(void *tty)
{
  l r;
  ul mask;
  void *argv[]={"/bin/agetty",tty,0};/*0 for envp is not "portable"*/

  OUT(PRE "getty %s...\n",tty);

  r=clone(SIGCHLD);
  if(ISERR(r)){
    OUT("ERROR(%ld):unable to clone for getty(%s)\n",r,tty);
    exit_group(-1);
  }

  if(r) return r;/*return the child process id*/

  mask=(~0);
  r=rt_sigprocmask(SIG_UNBLOCK,&mask,sizeof(mask));
  if(ISERR(r)){
    OUT("ERROR(%ld):unable to unblock all signals for getty(%s)\n",r,tty);
    exit_group(-1);
  }

  r=setsid();
  if(ISERR(r)){
    OUT("ERROR(%ld):unable to setsid the getty(%s) clone\n",r,tty);
    exit_group(-1);
  }

  r=execve("/bin/agetty",argv);
  if(ISERR(r)) {OUT("ERROR(%ld):unable to run /bin/agetty(%s)\n",r,tty);}
  exit_group(-1);
#ifdef __GNUC__
  return 0;/*unreachable not detected at the end of function*/
#endif
}
#endif

static void sysstart(void)
{
  i pid=sysstart_clone();
  l r=waitid(P_PID,pid,0,WEXITED);
  if(ISERR(r)){
    OUT("ERROR(%ld):unable to wait for /bin/sysstart to exit\n",r);
    exit_group(-1);
  }
}

#ifdef NO_TTY
/*minimal, we don't even have ttys to restart*/
static void main_loop(void)
{
  loop{
    l r=waitid(P_ALL,0,0,WEXITED);
    if(ISERR(r)){
      OUT("ERROR(%ld):unable to wait on orphan process terminations\n",r);
    }
  }
  unreachable();
}
#else
/*we allow ourself 2 ttys to restart*/
static void main_loop(void)
{
  i tty1=getty_spawn("tty1");
  i tty2=getty_spawn("tty2");

  loop{
    struct siginfo siginfo;
    l r=waitid(P_ALL,0,&siginfo,WEXITED);
    if(ISERR(r)){
      OUT("ERROR(%ld):unable to wait on /bin/agetty clone and orphan process terminations\n",r);
      exit_group(-1);
    }
         if(siginfo.fields.sigchld.pid==tty1) tty1=getty_spawn("tty1");
    else if(siginfo.fields.sigchld.pid==tty2) tty2=getty_spawn("tty2");
    /*ignore the other children*/
  }
  unreachable();
}
#endif

void _start(void)
{
#ifndef QUIET 
  static u8 dprintf_buf[DPRINTF_BUF_SZ];
  g_dprintf_buf=dprintf_buf;
#endif

  sigs_setup();
  sysstart();
  main_loop();
  unreachable();
}
